/*
 *  $Id: $
 *
 *  Part of INX: INterfaces in Xml.
 *  Copyright (C) 2007 David Griffiths
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package dg.inx;

//import com.michaelbaranov.microba.calendar.DatePicker;
//import com.michaelbaranov.microba.common.CommitListener;
import java.awt.Component;
import java.awt.ItemSelectable;
import java.awt.TextComponent;
import java.awt.event.ActionListener;
import java.awt.event.FocusListener;
import java.awt.event.ItemListener;
import java.beans.EventHandler;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Method;
import javax.swing.ComboBoxModel;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListDataListener;

public final class Controller {
    private AbstractModel model;

    public Controller(final AbstractModel theModel) {
        this.model = theModel;
    }

    private static String getSetter(final String propertyName) {
        return "set" + propertyName.substring(0, 1).toUpperCase()
        + propertyName.substring(1);
    }

    private static String getGetter(final String propertyName) {
        return "get" + propertyName.substring(0, 1).toUpperCase()
        + propertyName.substring(1);
    }

    private static String getIsser(final String propertyName) {
        return "is" + propertyName.substring(0, 1).toUpperCase()
        + propertyName.substring(1);
    }

    private static Method getMethod(final Object object,
            final String methodName) {
        try {
            Method[] methods = object.getClass().getMethods();
            Method method = null;
            for (int i = 0; i < methods.length; i++) {
                if (methodName.equals(methods[i].getName())) {
                    method = methods[i];
                    break;
                }
            }
            if (method == null) {
                throw new RuntimeException("Can't find method " + methodName
                        + " on " + object);
            }
            return method;
        } catch (Exception e) {
            throw new RuntimeException("Binding failed: " + e.getMessage());
        }
    }

    public void bind(final ItemSelectable view, final String modelProperty) {
        try {
            String viewProperty = "selected";
            Method viewSetter = getMethod(view, getSetter(viewProperty));
            Method modelGetter = null;
            try {
                modelGetter = getMethod(model, getGetter(modelProperty));
            } catch (Exception e) {
                modelGetter = getMethod(model, getIsser(modelProperty));
            }
            Object value = modelGetter.invoke(model, (Object[]) null);
            Object[] values = {value};
            if (value != null) {
                viewSetter.invoke(view, values);
            }
            bindImpl(view, viewProperty, model, modelProperty);
            bindImpl(model, modelProperty, view, viewProperty);
        } catch (Exception e) {
            throw new RuntimeException("Oops - " + e.getMessage());
        }
    }

//     public void bind(final DatePicker view, final String modelProperty) {
//         try {
//             System.out.println("binding date picker");
//             String viewProperty = "date";
//             Method viewSetter = getMethod(view, getSetter(viewProperty));
//             Method modelGetter = null;
//             try {
//                 modelGetter = getMethod(model, getGetter(modelProperty));
//             } catch (Exception e) {
//                 modelGetter = getMethod(model, getIsser(modelProperty));
//             }
//             Object value = modelGetter.invoke(model, (Object[]) null);
//             Object[] values = {value};
//             if (value != null) {
//                 viewSetter.invoke(view, values);
//             }
//             System.out.println("bindimpl 0");
//             bindImpl(view, viewProperty, model, modelProperty);
//             System.out.println("bindimpl 1");
//             bindImpl(model, modelProperty, view, viewProperty);
//             System.out.println("both finished ok");
//         } catch (Exception e) {
//             throw new RuntimeException("Oops - " + e.getMessage());
//         }
//     }

    public void bind(final JComboBox view, final String modelProperty) {
        try {
            String viewProperty = "selectedItem";
            Method viewSetter = getMethod(view, getSetter(viewProperty));
            Method modelGetter = null;
            try {
                modelGetter = getMethod(model, getGetter(modelProperty));
            } catch (Exception e) {
                modelGetter = getMethod(model, getIsser(modelProperty));
            }
            Object value = modelGetter.invoke(model, (Object[]) null);
            Object[] values = {value};
            if (value != null) {
                viewSetter.invoke(view, values);
            }
            bindImpl(view, viewProperty, model, modelProperty);
            bindImpl(model, modelProperty, (ItemSelectable) view, viewProperty);
        } catch (Exception e) {
            throw new RuntimeException("Oops - " + e.getMessage());
        }
    }

    public void bind(final JCheckBox view, final String modelProperty) {
        try {
            String viewProperty = "selected";
            Method viewSetter = getMethod(view, getSetter(viewProperty));
            Method modelGetter = null;
            try {
                modelGetter = getMethod(model, getGetter(modelProperty));
            } catch (Exception e) {
                modelGetter = getMethod(model, getIsser(modelProperty));
            }
            Object value = modelGetter.invoke(model, (Object[]) null);
            Object[] values = {value};
            if (value != null) {
                viewSetter.invoke(view, values);
            }
            bindImpl(view, viewProperty, model, modelProperty);
            bindImpl(model, modelProperty, (ItemSelectable) view, viewProperty);
        } catch (Exception e) {
            throw new RuntimeException("Oops - " + e.getMessage());
        }
    }

    public void bind(final JTextArea view, final String modelProperty) {
        try {
            String viewProperty = "text";
            Method viewSetter = getMethod(view, getSetter(viewProperty));
            Method modelGetter = null;
            try {
                modelGetter = getMethod(model, getGetter(modelProperty));
            } catch (Exception e) {
                modelGetter = getMethod(model, getIsser(modelProperty));
            }
            Object value = modelGetter.invoke(model, (Object[]) null);
            Object[] values = {value};
            if (value != null) {
                viewSetter.invoke(view, values);
            }
            bindImpl(view, viewProperty, model, modelProperty);
            bindImpl(model, modelProperty, view, viewProperty);
        } catch (Exception e) {
            throw new RuntimeException("Oops - " + e.getMessage());
        }
    }

    public void bind(final JTextField view, final String modelProperty) {
        try {
            String viewProperty = "text";
            Method viewSetter = getMethod(view, getSetter(viewProperty));
            Method modelGetter = null;
            try {
                modelGetter = getMethod(model, getGetter(modelProperty));
            } catch (Exception e) {
                modelGetter = getMethod(model, getIsser(modelProperty));
            }
            Object value = modelGetter.invoke(model, (Object[]) null);
            Object[] values = {value};
            if (value != null) {
                viewSetter.invoke(view, values);
            }
            bindImpl(view, viewProperty, model, modelProperty);
            bindImpl(model, modelProperty, view, viewProperty);
        } catch (Exception e) {
            throw new RuntimeException("Oops - " + e.getMessage());
        }
    }

    public void bind(final JSpinner view, final String modelProperty) {
        try {
            String viewProperty = "value";
            Method viewSetter = getMethod(view, getSetter(viewProperty));
            Method modelGetter = null;
            try {
                modelGetter = getMethod(model, getGetter(modelProperty));
            } catch (Exception e) {
                modelGetter = getMethod(model, getIsser(modelProperty));
            }
            Object value = modelGetter.invoke(model, (Object[]) null);
            Object[] values = {value};
            if (value != null) {
                viewSetter.invoke(view, values);
            }
            bindImpl(view, viewProperty, model, modelProperty);
            bindImpl(model, modelProperty, view, viewProperty);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Oops - " + e.getMessage());
        }
    }

    public void bind(final TextComponent view, final String modelProperty) {
        try {
            String viewProperty = "text";
            Method viewSetter = getMethod(view, getSetter(viewProperty));
            Method modelGetter = null;
            try {
                modelGetter = getMethod(model, getGetter(modelProperty));
            } catch (Exception e) {
                modelGetter = getMethod(model, getIsser(modelProperty));
            }
            Object value = modelGetter.invoke(model, (Object[]) null);
            Object[] values = {value};
            if (value != null) {
                viewSetter.invoke(view, values);
            }
            bindImpl(view, viewProperty, model, modelProperty);
            bindImpl(model, modelProperty, view, viewProperty);
        } catch (Exception e) {
            throw new RuntimeException("Oops - " + e.getMessage());
        }
    }

    public void bind(final Object target, final String targetProperty,
            final String itemProperty) {
        try {
            bindImpl(target, targetProperty, model, itemProperty);
        } catch (Exception e) {
            throw new RuntimeException("Oops - " + e.getMessage());
        }
    }

    private void bindImpl(final Object target, final String targetProperty,
            final AbstractModel source, final String itemProperty) {
        PropertyChangeListener pcl
                = (PropertyChangeListener) EventHandler.create(
                PropertyChangeListener.class, target, targetProperty,
                "source." + itemProperty, "propertyChange");
        source.addPropertyChangeListener(itemProperty, pcl);
    }

    private void bindImpl(final Object target, final String targetProperty,
            final Component source, final String itemProperty) {
        PropertyChangeListener pcl
                = (PropertyChangeListener) EventHandler.create(
                PropertyChangeListener.class, target, targetProperty,
                "source." + itemProperty, "propertyChange");
        source.addPropertyChangeListener(pcl);
    }

    private void bindImpl(final Object target, final String targetProperty,
            final ItemSelectable is, final String itemProperty) {
        is.addItemListener(
                (ItemListener) EventHandler.create(
                ItemListener.class,
                target,
                targetProperty,
                "source." + itemProperty
                )
                );
    }

    private void bindImpl(final Object target, final String targetProperty,
            final ComboBoxModel aModel, final String itemProperty) {
        aModel.addListDataListener(
                (ListDataListener) EventHandler.create(
                ListDataListener.class,
                target,
                targetProperty,
                "source." + itemProperty
                )
                );
    }
    private void bindImpl(final Object target, final String targetProperty,
            final JTextField view, final String itemProperty) {
        view.addFocusListener(
                (FocusListener) EventHandler.create(
                FocusListener.class,
                target,
                targetProperty,
                "source." + itemProperty
                )
                );
    }
//     private void bindImpl(final Object target, final String targetProperty,
//             final DatePicker view, final String itemProperty) {
//         view.addFocusListener(
//                 (FocusListener) EventHandler.create(
//                 FocusListener.class,
//                 target,
//                 targetProperty,
//                 "source." + itemProperty
//                 )
//                 );
//     }
    private void bindImpl(final Object target, final String targetProperty,
            final JTextArea view, final String itemProperty) {
        view.addFocusListener(
                (FocusListener) EventHandler.create(
                FocusListener.class,
                target,
                targetProperty,
                "source." + itemProperty
                )
                );
    }
    private void bindImpl(final Object target, final String targetProperty,
            final JSpinner view, final String itemProperty) {
        view.addChangeListener(
                (ChangeListener) EventHandler.create(
                ChangeListener.class,
                target,
                targetProperty,
                "source." + itemProperty
                )
                );
    }
    /**
     * If object source generates an action-event, then call the given method
     * in the target object. NB: source must contain an "addActionListener"
     * method for this to work.
     */
    public void bindMethod(final Object target, final String methodName,
            final Object source) {
        Class clazz = source.getClass();
        try {
            Method method = clazz.getMethod("addActionListener",
                    new Class[]{ActionListener.class});
            ActionListener actionListener
                    = (ActionListener) EventHandler.create(
                    ActionListener.class,
                    target,
                    methodName
                    );
            method.invoke(source, new Object[]{actionListener});
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
